﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.Windows.Media;
using Microsoft.Phone.Shell;

namespace Microsoft.Phone.Controls
{
    /// <summary>
    /// Bindable alternative <see cref="IApplicationBar">IApplicationBar</see> implementation.
    /// </summary>
    [ContentProperty("Buttons")]
    public class ApplicationBarHost : ItemsControl, IApplicationBar
    {
        private readonly ApplicationBar _applicationBar;

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty ApplicationBarOpacityProperty =
            DependencyProperty.RegisterAttached("ApplicationBarOpacity",
                                                typeof (double),
                                                typeof (ApplicationBarHost),
                                                new PropertyMetadata(1.0, OnApplicationBarOpacityChanged));

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty IsVisibleProperty =
            DependencyProperty.RegisterAttached("IsVisible",
                                                typeof (bool),
                                                typeof (ApplicationBarHost),
                                                new PropertyMetadata(true, OnIsVisibleChanged));

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty IsMenuEnabledProperty =
            DependencyProperty.RegisterAttached("IsMenuEnabled",
                                                typeof (bool),
                                                typeof (ApplicationBarHost),
                                                new PropertyMetadata(true, OnIsMenuEnabledChanged));

        /// <summary>
        /// Initializes new instance of class.
        /// </summary>
        public ApplicationBarHost()
        {
            _applicationBar = new ApplicationBar();
            Loaded += OnLoaded;
        }

        #region Implementation of IApplicationBar

        /// <summary>
        /// Gets or sets a value that indicates whether the Application Bar is visible.
        /// </summary>
        /// <returns>
        /// true if the Application Bar is visible; otherwise, false.
        /// </returns>
        public bool IsVisible
        {
            get { return (bool) GetValue(IsVisibleProperty); }
            set { SetValue(IsVisibleProperty, value); }
        }

        /// <summary>
        /// Gets or sets opacity.
        /// </summary>
        public double ApplicationBarOpacity
        {
            get { return (double) GetValue(ApplicationBarOpacityProperty); }
            set { SetValue(ApplicationBarOpacityProperty, value); }
        }

        /// <summary>
        /// Gets or sets a value that indicates whether the user can open the menu.
        /// </summary>
        /// <returns>
        /// true if the menu is enabled; otherwise, false.
        /// </returns>
        public bool IsMenuEnabled
        {
            get { return (bool) GetValue(IsMenuEnabledProperty); }
            set { SetValue(IsMenuEnabledProperty, value); }
        }

        /// <summary>
        /// Gets or sets the background color of the Application Bar.
        /// </summary>
        /// <returns>
        /// The background color of the Application Bar.
        /// </returns>
        public Color BackgroundColor
        {
            get { return _applicationBar.BackgroundColor; }
            set { _applicationBar.BackgroundColor = value; }
        }

        /// <summary>
        /// Gets or sets the foreground color of the Application Bar.
        /// </summary>
        /// <returns>
        /// The foreground color of the Application Bar.
        /// </returns>
        public Color ForegroundColor
        {
            get { return _applicationBar.ForegroundColor; }
            set { _applicationBar.ForegroundColor = value; }
        }

        /// <summary>
        /// Gets or sets the size of the Application Bar.
        /// </summary>
        /// <returns>
        /// One of the enumeration values that indicates the size of the Application Bar.
        /// </returns>
        public ApplicationBarMode Mode
        {
            get { return _applicationBar.Mode; }
            set { _applicationBar.Mode = value; }
        }

        /// <summary>
        /// Gets the distance that the Application Bar extends into a page when the <see cref="P:Microsoft.Phone.Shell.IApplicationBar.Mode"/> property is set to <see cref="F:Microsoft.Phone.Shell.ApplicationBarMode.Default"/>.
        /// </summary>
        /// <returns>
        /// The distance that the Application Bar extends into a page.
        /// </returns>
        public double DefaultSize
        {
            get { return _applicationBar.DefaultSize; }
        }

        /// <summary>
        /// Gets the distance that the Application Bar extends into a page when the <see cref="P:Microsoft.Phone.Shell.IApplicationBar.Mode"/> property is set to <see cref="F:Microsoft.Phone.Shell.ApplicationBarMode.Minimized"/>.
        /// </summary>
        /// <returns>
        /// The distance that the Application Bar extends into a page.
        /// </returns>
        public double MiniSize
        {
            get { return _applicationBar.MiniSize; }
        }

        /// <summary>
        /// Gets the list of the buttons that appear on the Application Bar.
        /// </summary>
        /// <returns>
        /// The Application Bar buttons.
        /// </returns>
        public IList Buttons
        {
            get { return Items; }
        }

        /// <summary>
        /// Gets the list of the menu items that appear on the Application Bar.
        /// </summary>
        /// <returns>
        /// The list of menu items.
        /// </returns>
        public IList MenuItems
        {
            get { return Items; }
        }

        /// <summary>
        /// Occurs when the user opens or closes the menu.
        /// </summary>
        public event EventHandler<ApplicationBarStateChangedEventArgs> StateChanged
        {
            add
            {
                if (_applicationBar != null)
                {
                    _applicationBar.StateChanged += value;
                }
            }
            remove
            {
                if (_applicationBar != null)
                {
                    _applicationBar.StateChanged -= value;
                }
            }
        }

        #endregion

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            var page = this.GetFirstOrDefaultVisualAncestor<PhoneApplicationPage>();

            if (page != null)
            {
                page.ApplicationBar = _applicationBar;
            }

            var applicationBarHost = sender as ApplicationBarHost;

            if (applicationBarHost != null)
            {
                applicationBarHost.Loaded -= OnLoaded;
            }
        }

        /// <summary>
        /// Gets <see cref="ApplicationBarIconButton">buttons</see> <see cref="IEnumerable{T}">enumeration</see>.
        /// </summary>
        /// <returns><see cref="ApplicationBarIconButton">Buttons</see> <see cref="IEnumerable{T}">enumeration</see>.</returns>
        protected virtual IEnumerable<ApplicationBarIconButton> GetButtons()
        {
            return Filter<ApplicationBarIconButtonHost>(Items).Select(e => e.Button).ToList();
        }

        /// <summary>
        /// Gets <see cref="ApplicationBarMenuItem">menu items</see> <see cref="IEnumerable{T}">enumeration</see>.
        /// </summary>
        /// <returns><see cref="ApplicationBarMenuItem">Menu items</see> <see cref="IEnumerable{T}">enumeration</see>.</returns>
        protected virtual IEnumerable<ApplicationBarMenuItem> GetMenuItems()
        {
            return Filter<ApplicationBarMenuItemHost>(Items).Select(e => e.MenuItem).ToList();
        }

        /// <summary>
        /// Filters items by specific type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="items"><see cref="object">Items</see> <see cref="IEnumerable{T}">enumeration</see>.</param>
        /// <returns>Filtered items <see cref="IEnumerable{T}">enumeration</see>.</returns>
        protected static IEnumerable<T> Filter<T>(IEnumerable<object> items) where T : class
        {
            return items == null ? Enumerable.Empty<T>() : items.OfType<T>().ToList();
        }

        /// <summary>
        /// Called when the value of the <see cref="P:System.Windows.Controls.ItemsControl.Items"/> property changes.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> that contains the event data</param>
        protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
        {
            base.OnItemsChanged(e);

            _applicationBar.Buttons.Clear();
            _applicationBar.MenuItems.Clear();

            foreach (var button in GetButtons())
            {
                _applicationBar.Buttons.Add(button);
            }

            foreach (var button in GetMenuItems())
            {
                _applicationBar.MenuItems.Add(button);
            }
        }

        private void OnApplicationBarOpacityChanged(double value)
        {
            _applicationBar.Opacity = value;
        }

        private void OnIsVisibleChanged(bool value)
        {
            _applicationBar.IsVisible = value;
        }

        private void OnIsMenuEnabledChanged(bool value)
        {
            _applicationBar.IsMenuEnabled = value;
        }

        private static void OnApplicationBarOpacityChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ApplicationBarHost) || (e.NewValue == e.OldValue)) return;

            (d as ApplicationBarHost).OnApplicationBarOpacityChanged((double) e.NewValue);
        }

        private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ApplicationBarHost) || (e.NewValue == e.OldValue)) return;

            (d as ApplicationBarHost).OnIsVisibleChanged((bool) e.NewValue);
        }

        private static void OnIsMenuEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ApplicationBarHost) || (e.NewValue == e.OldValue)) return;

            (d as ApplicationBarHost).OnIsMenuEnabledChanged((bool) e.NewValue);
        }
    }
}